package com.heima.comment.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.aliyun.AliyunGreenTemplate;
import com.heima.comment.model.entity.ApComment;
import com.heima.comment.model.entity.ApCommentLike;
import com.heima.comment.model.vo.ApCommentVo;
import com.heima.comment.service.ApCommentService;
import com.heima.comment.util.AppUserThreadLocalUtil;
import com.heima.common.constants.ApCommentConstant;
import com.heima.feign.api.ApUserFeignClient;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.entity.ApUser;
import org.apache.commons.lang.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class ApCommentServiceImpl implements ApCommentService {

    @Resource
    private AliyunGreenTemplate aliyunGreenTemplate;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private ApUserFeignClient apUserFeignClient;

    @Resource
    private MongoTemplate mongoTemplate;


    @Override
    public ResponseResult save(String targetId, String content, Short commentType) {
        //1. 校验参数
        if (StringUtils.isEmpty(targetId) || StringUtils.isEmpty(content)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //校验评论内容的长度不超过140
        if (content.length() > 140) {
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "评论长度不能超过140个字!");
        }

        ApUser user = AppUserThreadLocalUtil.get();
        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        //评论内容的安全检测
        Map<String, String> result = aliyunGreenTemplate.greenTextScan(content);
        if (!"pass".equals(result.get("suggestion"))) {
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "评论内容中包含敏感信息!");
        }

        //2. 查询用户详情
        String key = "USER_LOGIN_" + user.getId();
        String userInfo = stringRedisTemplate.opsForValue().get(key);
        //2.1 缓存中没有用户信息
        if (StringUtils.isNotEmpty(userInfo)) {
            user = JSON.parseObject(userInfo, ApUser.class);
        } else {
            ResponseResult<ApUser> responseResult = apUserFeignClient.queryById(user.getId());
            if (responseResult.getCode() != 200 || responseResult.getData() == null) {
                return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
            }

            user = responseResult.getData();
            stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(user), 1, TimeUnit.DAYS);
        }

        //3. 封装数据 , 保存到数据库
        ApComment apComment = new ApComment();
        apComment.setId(ObjectId.get().toHexString());
        apComment.setContent(content);
        apComment.setCreatedTime(new Date());
        apComment.setImage(user.getImage());
        apComment.setNickName(user.getNickname());
        apComment.setTargetId(targetId);
        apComment.setLikes(0);
        apComment.setReply(0);
        apComment.setUpdatedTime(new Date());
        apComment.setUserId(user.getId());

        mongoTemplate.save(apComment);

        //如果是评论回复 - 目标评论集合中评论的回复数量 + 1
        if (ApCommentConstant.COMMENT_TYPE_COMMENT.equals(commentType)) {
            Query query = Query.query(Criteria.where("id").is(targetId));
            UpdateDefinition update = new Update().inc("reply", 1);
            mongoTemplate.updateFirst(query, update, ApComment.class);
        }

        //4. 返回数据
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    @Override
    public ResponseResult like(String targetId, Short operation) {
        //1. 校验参数
        if (StringUtils.isEmpty(targetId)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        ApUser user = AppUserThreadLocalUtil.get();
        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        ApComment comment = mongoTemplate.findById(targetId, ApComment.class);
        if (comment == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //2. 判断用户是否已经点赞
        String comment_like_key = "COMMENT_LIKE_" + user.getId();
        Boolean isMember = stringRedisTemplate.opsForSet().isMember(comment_like_key, comment.getId());

        //2.1 如果用户已经点赞 ---- 点赞
        if (isMember && operation == 0) {
            return ResponseResult.okResult(comment.getLikes());
        }

        //2.2 如果用户已经点赞  --- 取消点赞
        if (isMember && operation == 1) {
            //2.2.1 删除缓存数据
            stringRedisTemplate.opsForSet().remove(comment_like_key, comment.getId());
            //2.2.2 删除mongodb数据
            mongoTemplate.remove(Query.query(Criteria.where("userId").is(user.getId()).and("targetId").is(targetId)), ApCommentLike.class);
            //2.2.3 扣减评论集合中 , 对应评论的点赞数
            //comment.setLikes(comment.getLikes() - 1 > 0 ? comment.getLikes() - 1 : 0);
            //mongoTemplate.save(comment);

            Query query = Query.query(Criteria.where("id").is(targetId).and("likes").gt(0));
            UpdateDefinition update = new Update().inc("likes", -1);
            mongoTemplate.updateFirst(query, update, ApComment.class);
        }

        //2.3 如果用户以前没有点赞 --- 点赞
        if (!isMember && operation == 0) {
            //2.3.1 保存数据到Redis
            stringRedisTemplate.opsForSet().add(comment_like_key, comment.getId());
            //2.3.2 保存数据到MongoDB
            ApCommentLike commentLike = new ApCommentLike();
            commentLike.setId(ObjectId.get().toHexString());
            commentLike.setUserId(user.getId());
            commentLike.setTargetId(targetId);
            commentLike.setCreatedTime(new Date());
            mongoTemplate.save(commentLike);

            //2.3.3 评论集合中 , 对应评论的点赞数 + 1
            Query query = Query.query(Criteria.where("id").is(targetId));
            UpdateDefinition update = new Update().inc("likes", 1);
            mongoTemplate.updateFirst(query, update, ApComment.class);
        }

        //3. 封装数据返回 ------ 点赞数
        comment = mongoTemplate.findById(targetId, ApComment.class);

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("likes", comment.getLikes());

        return ResponseResult.okResult(resultMap);
    }

    @Override
    public ResponseResult load(String targetId, Date minDate, Integer size) {
        //1. 校验参数
        if (targetId == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        if (size == null || size <= 0) {
            size = 10;
        }
        size = Math.min(size, 50);

        if (minDate == null) {
            minDate = new Date();
        }

        //2. 查询评论列表
        Query query = Query.query(Criteria.where("targetId").is(targetId).and("createdTime").lt(minDate)).with(Sort.by("createdTime").descending()).limit(10);
        List<ApComment> commentList = mongoTemplate.find(query, ApComment.class);

        //3. 回显用户评论点赞数据
        //3.1 用户未登录, 直接返回不需要回显
        ApUser user = AppUserThreadLocalUtil.get();
        if (user == null) {
            return ResponseResult.okResult(commentList);
        }
        //3.2 用户已登录需要回显
        String comment_like_key = "COMMENT_LIKE_" + user.getId();
        Set<String> likes_comment_ids = stringRedisTemplate.opsForSet().members(comment_like_key);
        if (CollectionUtils.isEmpty(likes_comment_ids)) {
            return ResponseResult.okResult(commentList);
        }

        List<ApCommentVo> commentVoList = commentList.stream().map(apComment -> {
            String id = apComment.getId();
            ApCommentVo apCommentVo = new ApCommentVo();
            BeanUtils.copyProperties(apComment, apCommentVo);
            if (likes_comment_ids.contains(id)) {
                apCommentVo.setOperation((short) 0);
            }

            return apCommentVo;
        }).collect(Collectors.toList());

        //4. 封装数据返回
        return ResponseResult.okResult(commentVoList);
    }
}
